// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImplModel.
//   $Id$



// Model.cpp: implementation of the CImplModel class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XForms.h"
#include "XFormsApp.h"
#include "resource.h"
#include "Model.h"
#include "FormControl.h"
#include <limits>
#include "ModelUI.h"
#include "output.h"
#include <Common/Internet/msw/InternetUtils.h>
#include "implInstance.h"
#include "ExecuteDispatch.h"
#include "XFormsXFormsElement.h"
#include "implbind.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

extern CXFormsApp theApp;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
/*
CImplModel::CImplModel()
{
	m_pInstance = NULL;
	m_pSchema = NULL;
}
*/
//CImplModel::CImplModel(CComObject<CXFormsXFormsElement> * pXForms):

CImplModel::CImplModel(CXFormsElementModel * pXFormsElement):
	m_pInstance(NULL)
	,m_pMasterDependencyList(NULL)
	,m_pPertinentSubGraph(0)
	,CImpl(static_cast<CXFormsElement *>(pXFormsElement))
	,m_bRefreshPending(false)
	,m_bRecalculatePending(true)
	,m_bRebuildPending(true)
	,m_bRevalidatePending(true)
	,m_bCreateDataFromFormControls(false)
	,m_bInitalised(false)
	,m_bSilentRunning(false)
	,m_bCreatingMDL(false)
	,m_bDoSilentUpdate(false)
	{
	//Not setModel(), we don't add ourselves to any lists.
	const_cast<CImplModel*>(m_pModel) = this;
	
	CoCreateInstance
	(	
		CLSID_XMLSchemaCache60,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IXMLDOMSchemaCollection2,
		(void**)&m_pSchemaCache
	);

	if(m_pSchemaCache)
	{
		CComPtr<IXMLDOMSchemaCollection> pGlobalCache;
		theApp.FormsManager().GetSchemaCache(&pGlobalCache);
		if(pGlobalCache)
			m_pSchemaCache->addCollection(pGlobalCache);
	}
}
#ifdef _DEBUG
	#define _DEBUG_CATCH_MODEL_DELETION_FC_CLEANUP_EXCEPTIONS_AT_SOURCE 1
#endif

CImplModel::~CImplModel()
{
	//Any dead controls will be in the 
	//	m_FormControls too, 
	m_DeadFormControls.RemoveAll();
	m_pParent = 0;
	m_pParentImpl = 0;

	while (!m_FormControls.IsEmpty())
	{
		CFormControl * pFc = m_FormControls.RemoveHead();
		if(pFc) {
#ifndef _DEBUG_CATCH_MODEL_DELETION_FC_CLEANUP_EXCEPTIONS_AT_SOURCE
			try
			{
#endif
				// Bug 1011 - As we don't delete CImplAction objects we should remove the 
				// association bewteen the object and this model. 
				pFc->setModel(0);
				//FUDGE: until actions can deregister, they should be
				//	deleted by their target.
				if(pFc && !dynamic_cast<CImplAction*>(pFc))
				{
					pFc->RequestDeleteAtEarliestConvenience();
				}
#ifndef _DEBUG_CATCH_MODEL_DELETION_FC_CLEANUP_EXCEPTIONS_AT_SOURCE
			}
			catch(...){ASSERT(0);}
#endif
		}
	}
	//all pointers in this list are in m_FormControls too.
	//	so don't delete them.
	m_pendingControls.RemoveAll(); 
	
	while(!m_Binds.IsEmpty())
	{
		try
		{
			delete m_Binds.RemoveHead();
		}
		catch(...)
		{
			ASSERT(0);
		}
	}


	//As the pointers and BSTRs are now in wrapper classes in the map, they will be released/freed by the dtor.
	m_Libraries.clear();

	
	CImplInstance * pInstance = 0;

	CMapBSTRToInstancePtr::iterator i = m_Instances.begin();
	while(i != m_Instances.end())
	{
		pInstance = i->second;
		delete(pInstance);
		++i;
	}
	m_Instances.clear();

	if(m_pXForms)
		m_pXForms->RemoveModel(this);
	
	m_pPertinentSubGraph = 0;
	if(m_pMasterDependencyList) {
		m_pMasterDependencyList->RequestDeleteAtEarliestConvenience();
		m_pMasterDependencyList = 0;
	}
}

HRESULT CImplModel::init(CImpl * pParent)
{
	HRESULT hr = S_OK;
	
	//must add even if there is a fatal error, otherwise we can't delete it
	//	on closedown.
	m_pXForms->AddModel(this);
	CImplModel * m;
	m_pXForms->getModel(m_sId,m);
	if(m == this)
	{
		hr = CImpl::init( pParent);

		if(!m_sId)
		{
			//TODO: this should be a parseError, to do that we need
			//		to seek the children we want rather than passively 
			//		checking all children.
			m_pXForms->reportException("id is compulsory on element Model");
			return E_FAIL;
		}

		hr = construct();
	}
	else
	{
		hr = E_FAIL;
	}
	return hr;
}

HRESULT CImplModel::construct(void)
{
	/*@spec
		4.2.1 xforms:modelConstruct
		Dispatched in response to: XForms Processor initialization.
		Target: model
		Bubbles: No
		Cancelable: No
		Context Info: None
	 */
	
	HRESULT hr = S_OK;
	//Execute any custom events and notify the host.
	hr = FireEvent(EVENT_MODELCONSTRUCT);
	//If the custom events were successful, 
	//	continue with default processing.
	if(SUCCEEDED(hr))
	{
		//load any function libraries needed for evaluating XPath expressions.
		hr = loadFunctions();
		if(SUCCEEDED(hr))
		{
			/*@spec		Default processing for this event results in the following:
						1.Schema is loaded, if any.
			*/
			hr = loadSchemas();
			if(SUCCEEDED(hr))
			{
				hr = loadInstances();
				
				if (SUCCEEDED(hr))
				{

					//This is called inside initialize, if it should be.
					//doRebuild();

					/*@spec
						3. Following this, an xforms:modelInitialize event
						is dispatched to element model.
					*/


					CImplInstance * pInstance = 0;

					CMapBSTRToInstancePtr::iterator i = m_Instances.begin();
					while(i != m_Instances.end() && SUCCEEDED(hr))
					{
						pInstance = i->second;
						++i;
						hr = pInstance->WaitForLoad();
					}

					if (SUCCEEDED(hr))
					{
						hr = initialize();
					}
					else
					{
						onLinkException(pInstance->m_sHref);	
					}
				}

			}
		}
	}
	hr = FireEvent(EVENT_MODELCONSTRUCTDONE);
	m_pXForms->modelConstructDone(this);
	return hr;
}

HRESULT CImplModel::initialize()
{
	m_pXForms->IncreaseDeferredUpdateCnt();
	HRESULT hr = rebuild(true);
	if(SUCCEEDED(hr))
	{
		CComPtr<IXmlState> pState;
		m_pMasterDependencyList->GetXMLState(&pState);
		{
			CComQIPtr<IDispatch> pDisp = pState;
			if(pDisp)
			{
				m_Libraries.insert(CPairBSTRIDispatchPtr(L"http://www.x-port.net/xmlstate",pDisp.p));
			}

			CMapCComBSTRToIDispatchPtr::iterator i = m_Libraries.find(L"urn:formsplayer.com");
			if(i != m_Libraries.end())
			{
				CComQIPtr<IDispatch> pDispFP = i->second;
				if(pDispFP)
				{
					CComPtr<IDispatch> pDispThisLib;
					m_pXForms->getFunctionLibrary(0,&pDispThisLib);
					CComVariant vtDisp(pDispThisLib);
					DISPPARAMS params = {&vtDisp,0,1,0};
					DISPID did = 0;
					CComVariant vtResult;
					::InvokeDispatch(pDispFP,L"addHelperLibrary",&params,&vtResult,did,DISPATCH_METHOD,LOCALE_SYSTEM_DEFAULT,0,0);
				}
			}
		}

		hr = recalculate(true);

		if(SUCCEEDED(hr))
			hr = revalidate(true); 
		if(SUCCEEDED(hr))
			hr = refresh(true); 
	}
	m_pXForms->DecreaseDeferredUpdateCnt();
	return hr;
}

HRESULT CImplModel::initializeSilently()
{
	HRESULT hr = S_FALSE;
	if(m_AsyncSubmissions.empty())
	{
		bool bCurrentSilenceState = m_bSilentRunning;
		m_bSilentRunning = true;
		m_pXForms->IncreaseDeferredUpdateCnt();
		hr = rebuild(true);
		if(SUCCEEDED(hr))
			hr = recalculate(true);
		if(SUCCEEDED(hr))
			hr = revalidate(true); 
		if(SUCCEEDED(hr))
			hr = refresh(true); 
		m_pXForms->DecreaseDeferredUpdateCnt();
		m_bSilentRunning = bCurrentSilenceState;
		m_bDoSilentUpdate = false;
	}
	else
	{
		m_bDoSilentUpdate = true;
	}
	return hr;
}

void CImplModel::AddAsyncSubmission(const CImplSubmitInfo* const pSubmission)
{
	m_AsyncSubmissions.push_back(pSubmission);
}

void CImplModel::RemoveAsyncSubmission(const CImplSubmitInfo* const pSubmission)
{
	m_AsyncSubmissions.remove(pSubmission);
	//emptiness of m_AsyncSubmissions is checked  inside initializesilently
	if(m_bDoSilentUpdate) // && m_AsyncSubmissions.empty())
	{
		initializeSilently();
	}

}

HRESULT CImplModel::doRecalculate()
{
	HRESULT hr = S_FALSE;
	if(m_bRecalculatePending)
	{
		m_bRecalculatePending = false;
		doRebuild();
		m_bRecalculatePending = false;
		//notify the host and run custom event handlers.
		if(!m_bSilentRunning)
			RunCustomBehaviour(EVENT_RECALCULATE);
		
		if(m_pPertinentSubGraph)
			m_pPertinentSubGraph->evaluate();
		
		m_pPertinentSubGraph = 0;
	
		m_bRecalculatePending = false;
		revalidate(true);
		// A recalculate results in a deferred refesh of that model.
		refresh(true);
		m_bRecalculatePending = false;
		hr = S_OK;
	}

	return hr;
}

HRESULT CImplModel::refresh(bool bDefer)
{
	m_bRefreshPending = true;
	HRESULT hr = S_OK;
	if(!bDefer)
	{
		m_pXForms->IncreaseDeferredUpdateCnt();
		hr = doRefresh();
		m_pXForms->DecreaseDeferredUpdateCnt();
	}
	return hr;
}

HRESULT CImplModel::doRefresh()
{
	HRESULT hr = S_OK;
	hr = burnDeadControls();
	if(SUCCEEDED(hr))
	{
		//clear the pending flags of any RRRs that need to occur prior to 
		//this
		if(m_bRefreshPending)
		{
 			m_bRefreshPending = false;
			doRebuild();
			doRecalculate();
			doRevalidate();
			m_bRefreshPending = true;
		}
		initializePendingControls();
		if(m_bRefreshPending)
		{
 			m_bRefreshPending = false;
			//alert the event system;
			//notify the host and run custom event handlers.
			if(!m_bSilentRunning)
				RunCustomBehaviour(EVENT_REFRESH);
			/*
			 * Allow each control to refresh itself
			 */
			POSITION pos = m_FormControls.GetHeadPosition();
	//pos=0;
			while (pos != NULL)
			{
				CImplNodeBinding * pFC = dynamic_cast<CImplNodeBinding*>(m_FormControls.GetNext(pos));
				if(pFC && !pFC->m_bInParentContext)
				{
					pFC->refresh();
				}
			}

			//To ensure that all the correct refreshes have occured prior to updateAppearance
			//	run through the loop again, updating the appearance of the controls.
			//Do not collapse this loop into the one above!
			//	It is possible for the order to be significant.  If a control is in the context
			//	of a parent, but occurs before the parent in the list, then its appearance
			//	will be updated before it has been refreshed.  This will lead to a lag in
			//	the representation of MIP states.
			pos = m_FormControls.GetHeadPosition();
			while (pos != NULL)
			{
				CImplNodeBinding * pFC = dynamic_cast<CImplNodeBinding*>(m_FormControls.GetNext(pos));
				if(pFC)
				{
					if(pFC->m_pXFormsElement)
						pFC->m_pXFormsElement->updateAppearance();
				}
			}
			//
			m_bRefreshPending = false;
			//clear the pending flags of any RRRRs that have been signalled 
			// by this refresh.  Do not recurse!  If recursion is permitted, 
			//Or even "iteration until it has all finished" or whatever other
			// phrase for recursive behaviour you want to use, it WILL go infinite.	
			//
			//The reason for this mess is that we no longer maintain the state
			//	of any controls 'live', and all of the status events occur during 
			//	refresh.  
			//The upshot of this is that if any change to the data occurs
			//	during any of these events, which would cause a prior event to occur
			//  it must be prevented, otherwise infinite recursion is likely.
			//On the other hand, it must be permitted, because without it, the data 
			//	and the UI will be out-of-sync.
			doRebuild();
			doRecalculate();
			doRevalidate();
			
			if(m_bRefreshPending)
			{
				POSITION pos = m_FormControls.GetHeadPosition();
				while (pos != NULL)
				{
					CImplNodeBinding * pFC = dynamic_cast<CImplNodeBinding*>(m_FormControls.GetNext(pos));
					if(pFC)
					{
						if(!pFC->m_bInParentContext)
							pFC->refresh();
					}
				}
			}
			m_bRefreshPending = false;
		}
	}
	return hr;
}

HRESULT CImplModel::reset()
{
	HRESULT hr = S_OK;

	/*
	 * Set the instance data back to its original values
	 * [TODO]
	 * Need to sort out event handling properly, since this is the only event where we call our custom
	 * events AFTER we have done the default event. We need to do this so that we can set up default values
	 * and such like, after doing a reset.
	 */
	CImplInstance * pInstance = 0;

	CMapBSTRToInstancePtr::iterator i = m_Instances.begin();
	while(i != m_Instances.end() && SUCCEEDED(hr))
	{
		pInstance = i->second;
		++i;
		pInstance->reset();
	}

	FireEvent(EVENT_RESET);

	//Deferred update  is switched off for reset.
	rebuild(false);
//	doRebuild();
	recalculate(false);
//	doRecalculate();
	revalidate(false);
//	doRevalidate();
	refresh(false);
//	doRefresh();

	return hr;
}

HRESULT CImplModel::revalidate(bool bDefer)
{
	m_bRevalidatePending = true;
	HRESULT hr = S_OK;
	if(!bDefer)
		hr = doRevalidate();
	return hr;
}


HRESULT CImplModel::doRevalidate()
{
	HRESULT hr = S_OK;
	if(m_bRevalidatePending && m_pMasterDependencyList)
	{
		m_pXForms->IncreaseDeferredUpdateCnt();
		m_bRevalidatePending = false;
		//notify the host and run custom event handlers.
		if(!m_bSilentRunning)
			RunCustomBehaviour(EVENT_REVALIDATE);
		m_pMasterDependencyList->validate();
		
		m_bRevalidatePending = false;
		m_pXForms->DecreaseDeferredUpdateCnt();
	}
	return hr;
}



HRESULT CImplModel::UIInitialize()
{

	/*@spec
		4.2.4 xforms:UIInitialize
		Dispatched in response to:
			XForms Processor user interface initialization.
		Target: model
		Bubbles: No
		Cancelable: No
		Context Info: None
		Default processing for this event results in the following:
			The host processor traverses the containing document,
			and for each form control, dispatches a
			xforms:formControlInitialize event to the form control.
	*/
	HRESULT hr = S_OK;
	
	//DONE: Confirm order of custom & default event processing
	//	I strongly suspect that custom comes first as many of
	//	these initialisation events fire other events
	//	eg. modelinitialize->UIInitialize->initializeDone
	
	
	// REMOVED: xforms-ui-initialize no longer part of the xforms spec....
	// hr = FireEvent(EVENT_UIINITIALIZE);

	POSITION pos = m_FormControls.GetHeadPosition();
	
	/*
	 * Allow each control to initialise its UI
	 */

	bool bForceInitialize = m_pXForms->m_bDocumentInitialised;
	
	while (pos != NULL && m_pXForms->canContinue())
	{
		// Changed from below due to UIScripts being overwritten as in the case of datetime  
		// and boolean types for input fields. The overwriting of the UIScripts can result in items
		// in the list being removed. In the example I found the last item was removed after the 
		// call to initialize for the  previous item. This resulted in a crash as the pos had now
		// been removed.
		//
		// CImplNodeBinding * pControl = (CImplNodeBinding*)m_FormControls.GetNext(pos);

		CImplNodeBinding * pControl = (CImplNodeBinding*) m_FormControls.GetAt(pos);
		

		// After submit where replace= instance, everything is reinitialized.
		//	So whenever model::uiinitialize occurs, Force the initialisation
		//	of FormControls.

		if(pControl && !pControl->m_bInParentContext)
			pControl->initialize(bForceInitialize);
		// Get the next item (form control) from the corrent  position.
		m_FormControls.GetNext(pos);
	}
	//this recalculate should not set the deferred refresh flag.
	recalculate(m_pXForms->IsInDeferredUpdate());
	m_bRefreshPending =false;
	// Indicates that the model has been Initalised and that any newly added xforms 
	// elements should handle the call to copyStyle(). Bug 187.
	m_bInitalised = true; 
	return hr;
}

HRESULT CImplModel::validate()
{
	HRESULT hr = S_OK;

	return hr;
}

HRESULT CImplModel::getInstanceNumber(BSTR sXPath, double* pdData,IXMLDOMNode * pContext)
{
	HRESULT hr = S_FALSE;
	CComPtr<IXMLDOMNode> pNode;
	if(pContext)
		pNode = pContext;
	else
		GetDefaultEvaluationContext(&pNode);

	if (m_pInstance)
		hr = m_pInstance->getInstanceNumber(sXPath,pNode, pdData);
	return hr;
}

HRESULT CImplModel::getInstanceData(BSTR sXPath, CNode * pContext,IXPathResult ** ppData)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
		hr = m_pInstance->getInstanceData(sXPath, pContext, ppData);

	return hr;
}



bool CImplModel::getInstanceBoolean(IXMLDOMNode * pContext,BSTR sRef)
{
	//Unused?
	if (m_pInstance)
		return m_pInstance->getInstanceBoolean(pContext,sRef);
	else
		return false;
}

HRESULT CImplModel::getInstanceNode(BSTR sXPath, CNode* pContextNode, bool bCreateIfNotFound,CNode** ppNode)
{
	HRESULT hr = S_FALSE;
	if(wcscmp(L".",sXPath)==0 && pContextNode)
	{
		//Shortcut for ".", saves messing about
		//	in getXPathResult.
		hr = S_OK;
		*ppNode = pContextNode;
		pContextNode->AddRef();
	}
	else
	{
		if (m_pInstance)
		{
			hr = m_pInstance->getInstanceNode(sXPath, pContextNode, ppNode);

			if(!*ppNode && bCreateIfNotFound && m_bCreateDataFromFormControls)
			{
				m_pInstance->CreateNode(pContextNode,sXPath,ppNode);
			}
		}
	}
	return hr;
}


HRESULT CImplModel::getInstanceNodeList(BSTR sXPath, CNode* pContextNode, bool bCreateIfNotFound, IXPathResult** ppResult)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
	{
		CComPtr<IXPathResult> pResult;
		hr = m_pInstance->getInstanceNodeList(sXPath, pContextNode, &pResult);
		if(bCreateIfNotFound && m_bCreateDataFromFormControls)
		{
			unsigned long len;
			pResult->get_snapshotLength(&len);
			if(!len)
			{
				pResult.Release();
				CNode * pNode = 0;
				m_pInstance->CreateNode(pContextNode,sXPath,&pNode);
				::safeRelease(pNode);
				hr = m_pInstance->getInstanceNodeList(sXPath, pContextNode, &pResult);
			}
		}
		pResult.CopyTo(ppResult);
	}
	return hr;

}

HRESULT CImplModel::putInstanceString(BSTR sXPath, CNode* pContextNode, BSTR sData)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
		hr = m_pInstance->putInstanceString(sXPath, pContextNode, sData);

	return hr;
}
#if 0
HRESULT CImplModel::putInstanceString(BSTR sXPath, BSTR sData)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
		hr = m_pInstance->putInstanceString(sXPath, NULL, sData);

	return hr;
}
#endif
HRESULT CImplModel::putInstanceData(CNode* pRecipientNode, IXPathResult * pData)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
		hr = m_pInstance->putInstanceData(pRecipientNode, pData);

	return hr;
}

struct vertexPair
{
	vertexPair(CVertex * a,CVertex* b)
	{
		p1 = a;p2 = b;
	}
	CVertex * p1;
	CVertex * p2;
};

void CImplModel::createPertinentDependencySubgraph(CTypedPtrList<CPtrList,CVertex*> * arLc,/*out*/CTypedPtrList<CPtrList,CVertex*> * arSubgraph)
{
	
	CTypedPtrList<CPtrList,vertexPair*> arStack;
	POSITION pos = arLc->GetHeadPosition();
	while (pos)
	{
		CVertex * r = arLc->GetNext(pos);

		if (!r->m_bVisited) 
		{
			arStack.AddTail(new vertexPair(NULL, r));
			
			while (!arStack.IsEmpty())
			{
				vertexPair * arTemp = arStack.RemoveTail();
				CVertex * v = arTemp->p1;
				CVertex * w = arTemp->p2;
				delete arTemp;
				CVertex * wS = NULL;
				if (!w->m_bVisited) 
				{
					w->m_bVisited = true;
					
					wS = m_pMasterDependencyList->createVertex(w->m_pInstanceNode, w->m_type,w->m_pIdResolver,this);

					wS->m_pIndex = w;
					w->m_pIndex = wS;
					arSubgraph->AddTail(wS);
					POSITION stackPos = w->m_lDepList.GetHeadPosition();

					while(stackPos)
						arStack.AddTail(new vertexPair(w, w->m_lDepList.GetNext(stackPos)));
				}//if (w has not been visited)
				else
					wS = w->m_pIndex;

				if (v) {
					CVertex * vS = v->m_pIndex;
					vS->addDependent(wS);
					wS->m_lInDegree++;
				}
			}//while (the stack is not empty)
		}//if (r is not visited)

		/*
		 * Now clear the visited flags set in the loop above
		 */
		POSITION SubPos = arSubgraph->GetHeadPosition();
		while (SubPos)
		{
			arSubgraph->GetNext(SubPos)->m_pIndex->m_bVisited = false;
		}
	}//for (each vertex in the change list)

}


void CImplModel::createMasterDependencyList()
{
	if(!m_bCreatingMDL)
	{
		m_bCreatingMDL = true;
		if(m_pMasterDependencyList)
		{
			//To ensure that no double delete shenannigans occur from a recursive entry into this 
			//	function, nullify m_pMasterDependencyList, before requesting its deletion.
			CVertexList * p  = m_pMasterDependencyList;
			m_pMasterDependencyList = 0;

			p->RequestDeleteAtEarliestConvenience();
		}

		m_pMasterDependencyList = new CVertexList();

		//Only binds are done at this stage.  
		//	Controls are done at the FormControlInitialize phase.
		POSITION pos = m_Binds.GetHeadPosition();
		while(pos)
		{
			m_Binds.GetNext(pos)->AddToGraph(m_pMasterDependencyList);
		}
		m_bCreatingMDL = false;
	}
	
	//return oVertexList;
}

HRESULT CImplModel::refreshNodeBindings()
{
	HRESULT hr = S_OK;
	return hr;	
}
#define IGNORE_REMOVECONTROL_MULTIFIND 0
HRESULT CImplModel::RemoveFormControl(CFormControl * pFC)
{
	POSITION pos = m_FormControls.Find(pFC);
	HRESULT hr;
	if(pos)
	{
		while(pos)
		{
			m_FormControls.RemoveAt(pos);
			pos = m_FormControls.Find(pFC);
			if(pos)
				ASSERT(IGNORE_REMOVECONTROL_MULTIFIND);
		}
		hr = S_OK;
		//Try to remove from pending controls too,
		//	The caller doesn't care whether this
		//	succeeds or not.
		pos = m_pendingControls.Find(pFC);
		while(pos)
		{
			m_pendingControls.RemoveAt(pos);
			pos = m_pendingControls.Find(pFC);
			if(pos)
				ASSERT(IGNORE_REMOVECONTROL_MULTIFIND);
		}
	
	}
	else
	{
		hr = S_FALSE;
	}
	return hr;
}

HRESULT CImplModel::rebuild(bool bDefer) 
{
	m_bRebuildPending = true;
	HRESULT hr = S_OK;
	if(!bDefer)
	{
		if(m_pXForms)
		{
			m_pXForms->IncreaseDeferredUpdateCnt();
			hr = doRebuild();
			m_pXForms->DecreaseDeferredUpdateCnt();
		}
	}

	return hr;
}

HRESULT CImplModel::recalculate(bool bDefer) 
{
	m_bRecalculatePending = true;
	HRESULT hr = S_OK;
	if(!bDefer)
	{
		m_pXForms->IncreaseDeferredUpdateCnt();
		hr = doRecalculate();
		m_pXForms->DecreaseDeferredUpdateCnt();
	}
	return hr;
}

HRESULT CImplModel::doRebuild() 
{
	HRESULT hr = S_FALSE;
	if(m_bRebuildPending)
	{
		m_bRebuildPending = false;
		//notify the host and run custom event handlers.
		if(!m_bSilentRunning)
			RunCustomBehaviour(EVENT_REBUILD);
		
 		//Watch for duplication of effort here, 
 		//	rebuild is called as part of initialise
 		//	but after all binds have been evaluated.
		m_pPertinentSubGraph = 0;
		createMasterDependencyList();
		//Set the pertinent dependency graph to masterDependencyList
		// so that all recalculations can occur.
		m_pPertinentSubGraph = m_pMasterDependencyList;
		if(!m_pXForms->m_bDocumentInitialised)
		{
			doRecalculate();
		}
 		POSITION pos1 = m_FormControls.GetHeadPosition();
		POSITION pos2;
		
 		while (( pos2 = pos1 ) != NULL && m_pXForms)
		{
			CImplNodeBinding * pControl =0;
			//Should be stored as CImplNodeBinding!
			CFormControl * pFormControl = m_FormControls.GetNext(pos1);
			try
			{
				if(pFormControl)
 					pControl = dynamic_cast<CImplNodeBinding*>(pFormControl);
			}
			catch(...)
			{
				//HACK:  remove deleted node from m_FormControls.
				//an error casting pFormControl indicates that it has been deleted.  
				//	This should be impossible, as there are plenty of places where it
				//	should get removed (DTor, setModel.), and for most people, it is.
				//	Unfortunately, Al and the Frenchmen seem to suffer from it, 
				//	The ideal, eventual solution, when I don't have critical issues coming out of my
				//	ears, or when I have much easier access to a machine that reproduces this issue,
				//	would be to get to the bottom of it, and/or (as the comment above suggests) store these 
				//	as CImplNodeBinding pointers.
				//however, neither of those states are currently true, so here is the gaffer-tape solution
			
			//Curiously, In the situation described in 
			//		http://www.formsplayer.com/node/1044
			//These are (apparently) not deleted, but uninitialized.  
			//	(on a debug build, the debugger reports pFormControl here to be 0xcdcdcdcd)
			//I can't see a way for uninitialized pointers to get added here, as (AFAIK) the only calls
			//	to AddFormControl are with the respective "this" as an argument.

				
			//fascinating - calling RemoveAt causes a crash - not in RemoveAt, but within its call stack
			//	in DOM2Events destruction of globals.  I fancy that this enters some message 
			//	loop which allows some mysterious unload to occur.
			//	m_FormControls.RemoveAt(pos2);
				continue;
			}

			if(pControl && !pControl->m_bInParentContext) {
				// HACK: This test prevents a crash, but I'm not sure which control the crash is on.
				//       The original intention was for m_vertices.RemoveAll() to be done on all
				//       controls, but it caused a crash on unload of my XForms information
				//       exposition form. Since the original bug (fixed by the RemoveAll) was on a
				//       <select1>, I have left the change in conditionally on that control type.
				//       Eventually, we should get to the root cause and do m_vertices.RemoveAll()
				//       in all cases where necessary and not those where it should not be done.

				// Original hack caused bug 401.  
				//		Formerly, m_vertices.RemoveAll was called to prevent a problem in buildDependencies
				//		Now, the condition has been inverted, and buildDependencies is called when false
				//		(former true condition).
			
				// SemiHack Caused bug 419.  
				//		I have reinstated The original hack but refined the test to prevent bug 401.
				if(!pControl->m_pBind && pControl->GetControlType() && wcscmp(pControl->GetControlType(), L"<select1>") == 0)
					pControl->m_vertices.RemoveAll();

				pControl->buildDependencies();
			}
 		}

		// Refresh of a model results in a deferred recalculate.
		recalculate(true);
        hr = S_OK;
	}
	return hr;
}

void CImplModel::AddBind(CImplBind *pBind)
{
	m_Binds.AddTail(pBind);
	//TODO: throw error for duplicate id.
	if(pBind->m_sId)
		m_pXForms->m_Binds.SetAt(CString(pBind->m_sId),pBind);

}

HRESULT CImplModel::addVertex
(
	IXMLDOMNode * pContext,
	CComBSTR sRef,
	bool bApplyFirstNodeRule,
	CVertex::EType eType,
	CVertex * vDependent,
	CTypedPtrArray<CPtrArray,CVertex*>  * pListToFill
	,CIdResolver * pResolver
)
{
	HRESULT hr = S_FALSE;
	//wrapper for child addvertex
	if(m_pMasterDependencyList)
	{
		hr = m_pMasterDependencyList->addVertex(this,pContext,sRef,bApplyFirstNodeRule,eType,vDependent,pListToFill,pResolver,true);
	}
	return hr;
}

void CImplModel::onBindingException(BSTR const sStatement, const BSTR sReason)
{
	//notify the host and run custom event handlers.
	FireEvent(EVENT_BINDINGEXCEPT,true,false);

	//report that there has been a binding exception.
	//format the error message:
	CComBSTR bsErrTemplate;
	bsErrTemplate.LoadString(IDS_MSG_BINDINGEXCEPTION);
	CString sErrMsg;
	sErrMsg.Format(CString(bsErrTemplate),EVENT_BINDINGEXCEPT,sStatement,sReason);
	//display it.
	m_pXForms->reportException(sErrMsg);
}

void CImplModel::onLinkException(BSTR const  sURI)
{
	//notify the host and run custom event handlers.
	FireEvent(EVENT_LINKEXCEPT,true,false,(LPVOID)sURI);

	//report that there has been a link exception.
	//format the error message:
	CComBSTR bsErrTemplate;
	bsErrTemplate.LoadString(IDS_MSG_LINKEXCEPTION);
	CString sErrMsg;
	sErrMsg.Format(CString(bsErrTemplate),EVENT_LINKEXCEPT,sURI);
	//display it.
	m_pXForms->reportException(sErrMsg);
}

void CImplModel::onLinkError(BSTR const  sURI)
{
	//notify the host and run custom event handlers.
	RunCustomBehaviour(EVENT_LINKERROR);
	//report that there has been a link error.
	//format the error:
	CComBSTR bsErrTemplate;
	bsErrTemplate.LoadString(IDS_MSG_LINKERROR);
	CString sErrMsg;
	sErrMsg.Format(CString(bsErrTemplate),EVENT_LINKERROR,sURI);
	//display it.
	m_pXForms->reportError(sErrMsg);
}


void CImplModel::notifyDestroy()
{
	//This event has to be fired before the destruction of any models,
	//	rather than in the actual destructor,
	//	otherwise cross-model action will crash.

	//notify the host and run custom event handlers.
	FireEvent(EVENT_MODELDESTRUCT);
}

HRESULT CImplModel::loadSchema(BSTR bsURL)
{
	HRESULT hr = E_FAIL;
	CComQIPtr<IXMLDOMDocument2> pDoc;
	if(bsURL[0]==L'#')
	{
		CComBSTRList::const_iterator i = m_lstAddedSchemata.begin();
		bool bAlreadyThere = false;
		while(i != m_lstAddedSchemata.end())
		{
			if(*i == &bsURL[1])
			{
				bAlreadyThere = true;
				break;
			}
			++i;
		}
		if(bAlreadyThere)
		{
			hr = S_FALSE;
		}
		else
		{
			hr = m_pXForms->GetInlineSchema(&bsURL[1],&pDoc);
			if(FAILED(hr))
				onLinkException(bsURL);
			else
				m_lstAddedSchemata.push_back(&bsURL[1]);
		}
	}
	else
	{
		CComBSTR sURL;
		sURL.Attach(Relative2AbsolutePath(m_pXForms->m_sCurrentURL,bsURL));

		CDOM3Document doc;
		VARIANT_BOOL b = VARIANT_FALSE;
	
		
		if(sURL == wcsstr(sURL,L"http://"))
		{
			void *pData = 0;
			DWORD dwSize = 0;
			char * pcURL = ::BSTRToStr(sURL);
			xport::ReadFromInternet(pcURL,&pData,dwSize);
			delete [] pcURL;
			CComBSTR bsData = reinterpret_cast<char *>(pData);
			
			doc.m_pXMLParser->put_async(VARIANT_FALSE);
			doc.m_pXMLParser->put_validateOnParse(VARIANT_FALSE);
			doc.m_pXMLParser->loadXML(bsData,&b);
			
		}
		//Fallback, in case ReadFromInternet returns gobbledegook (i.e. unexpected charset)
		if(!b)
		{
			 doc.m_pXMLParser->load(CComVariant(sURL),&b);
		}
		if(b)
		{
			pDoc = doc.m_pXMLParser;
		}
		else
		{
			CComPtr<IXMLDOMParseError> pErr;
			doc.m_pXMLParser->get_parseError(&pErr);
			long l;
			pErr->get_errorCode(&l);
			if(-2146697210==l)
			{
				//== could not open
				onLinkException(sURL);
			}
			else
			{
				CComBSTR sLoc,sReason;
				pErr->get_srcText(&sLoc);
				pErr->get_reason(&sReason);
				hr = FP_E_BADLYFORMED_SCHEMA;
				theApp.FormsManager().GetErrorSource().SetupErrorInfo(hr,bsURL, m_sId,sReason,sLoc);
			}
		}
	}

	CComBSTR sTargetURI;
	if(pDoc)
	{
		if(m_pXForms->canContinue())
		{
			CComPtr<IXMLDOMNode> pNode;
			if(SUCCEEDED(pDoc->selectSingleNode(_bstr_t("/*/@targetNamespace"),&pNode)) && pNode != NULL)
			{
				//use the target namespace of the document
				pNode->get_text(&sTargetURI);
			}
			else
			{
				sTargetURI = CComBSTR("");
			}
			pDoc->put_resolveExternals(VARIANT_TRUE);
			hr = m_pSchemaCache->add(sTargetURI,CComVariant(pDoc));
			if(FAILED(hr))
			{
				CComPtr<IErrorInfo> pErr;
				::GetErrorInfo(0,&pErr);
				CComBSTR sReason;
				if(pErr)
					pErr->GetDescription(&sReason);
				hr = FP_E_INVALID_SCHEMA;
				theApp.FormsManager().GetErrorSource().SetupErrorInfo(hr,bsURL, m_sId,sReason);
			}
		}
	}
	//I don't think this condition should ever have been reachable.
	//An exception would have been reached sooner if the schema
	//	were badly formed.
/*	else if(m_pXForms->canContinue())
	{
		CString sExcep;
		sExcep.Format("schema \"%S\" for model \"%S\" is not well formed",bsURL, m_sId);
		m_pXForms->reportException(sExcep);

	}*/
	return hr;
}

struct SThreadLoadIntanceInfo
{
	HANDLE m_hSignal;
	CImplInstance * m_pInstance;
};
/*
DWORD WINAPI ThreadLoadInstance(LPVOID vdArg)
{
	
	SThreadLoadIntanceInfo * pInfo = reinterpret_cast<SThreadLoadIntanceInfo*>(vdArg);
	HRESULT hr = pInfo->m_pInstance->GetContent();
	SetEvent(pInfo->m_hSignal);
	delete pInfo;
	ExitThread(hr);
	return 0;
}
*/
HRESULT CImplModel::loadInstances()
{
	/*@spec
		2.	An XPath data model is constructed from the instance data,
			according to the following rules:
				From an external source
				If there is no reference to an external instance,
					from an inline instance

		Note:
			If neither of these are supplied, the instance
			is constructed from the user interface, 
			during user interface construction.
	*/
	HRESULT hr = S_FALSE;
	CImplInstance * pInstance = 0;
	CMapBSTRToInstancePtr::iterator i = m_Instances.begin();
	if(i != m_Instances.end() )
	{
		while(i != m_Instances.end() && SUCCEEDED(hr))
		{
			pInstance = i->second;
			++i;
			hr = pInstance->GetContent();
		}

		if(FAILED(hr) && m_pXForms->canContinue())
		{
			CComBSTR sReason,sSrc;
			pInstance->m_pLastError->get_reason(&sReason);
			pInstance->m_pLastError->get_srcText(&sSrc);

			CString sErr;
			sErr.Format("Failed to load instance \"#%S\", <br /> Reason:%S<br />at:%S",pInstance->m_sId,sReason,sSrc);
			m_pXForms->reportException(sErr);
		}
	}
	else
	{
		CImplInstance * pInstance = new CImplInstance(this);
		//Don't parse, as there is nothing to parse, 
		//	instead, insert the default values and init as normal.
		m_sId.CopyTo(&pInstance->m_sId);
		pInstance->m_sId +=  L"instanceData";
		pInstance->m_sContent = L"<instanceData />";
		pInstance->init(this);
		addInstance(pInstance->m_sId , pInstance);
		hr = pInstance->GetContent();
		m_bCreateDataFromFormControls = true;
	}
	return hr;
}

HRESULT CImplModel::loadSchemas()
{
	HRESULT hr = S_FALSE;
	if(m_pSchemaCache)
	{
		CString sSchemaURLs(m_sSchemaURLs);
		CString sSchemaURL;
		int iSpace = sSchemaURLs.Find(' ');
		while(iSpace > -1 && SUCCEEDED(hr))
		{
			sSchemaURL = sSchemaURLs.Left(iSpace);
			sSchemaURL.Remove(' ');
			BSTR s = sSchemaURL.AllocSysString();
			hr = loadSchema(s);
			::SysFreeString(s);
			sSchemaURLs.Delete(0,iSpace+1);
			iSpace = sSchemaURLs.Find(' ');
		}
		sSchemaURLs.Remove(' ');
		//Do the last one.
		if(SUCCEEDED(hr) && !sSchemaURLs.IsEmpty())
		{
			sSchemaURL = sSchemaURLs;
			BSTR s = sSchemaURL.AllocSysString();
			hr = loadSchema(s);
			::SysFreeString(s);
		}

		if(FAILED(hr) && m_pXForms->canContinue())
		{
			m_pXForms->reportException(hr);
		}
		
		//Don't bother with this loop if there are no schemata to apply.
		if(SUCCEEDED(hr))
		{
			CImplInstance * pInstance = 0;
			CMapBSTRToInstancePtr::iterator i = m_Instances.begin();
			while(i != m_Instances.end() && SUCCEEDED(hr))
			{
				pInstance = i->second;
				++i;
				pInstance->put_schemaCache(m_pSchemaCache);
			}
		}
	}

	return hr;
}
#include "dispex.h"
HRESULT CImplModel::loadFunctions()
{
	HRESULT hr = S_FALSE;
	//The built in XForms functions are loaded elsewhere,
	//	this is because they arerelevant at the whole form
	//	level, not the model (index)

	//run through the list, breaking at spaces
	if(m_sExtensionFunctions.m_str)
	{
		CComPtr<IXPathNSResolver> pNSResolver;
		hr = m_pXForms->GetNSResolver(&pNSResolver);
		if(SUCCEEDED(hr) && pNSResolver)
		{

			wchar_t * wFunction = wcstok(m_sExtensionFunctions.m_str, L" ");

			while(wFunction)
			{
				//Look for the colon, we can't use wcstok again, 
				//	as it uses a static variable.
				wchar_t * wColon = wcschr(wFunction,L':');

				if(wColon)
				{
					//if there is a namespace prefix, look it up.
					*wColon = 0;
					//wFunction now ends at the colon.
					CComBSTR bsURI;
					CComBSTR sPrefix(wFunction);
					pNSResolver->lookupNamespaceURI(sPrefix,&bsURI);

					CMapCComBSTRToIDispatchPtr::iterator i = m_Libraries.find(bsURI);
					CComQIPtr<IDispatch> pDisp;
					if(i == m_Libraries.end())
					{
						m_pXForms->getFunctionLibrary(bsURI,&pDisp);
						if(pDisp)
						{
							//If we have successfuly retreived a function library
							//	from the controlling application, add it to our list.
							m_Libraries.insert(CPairBSTRIDispatchPtr(bsURI.Detach(),pDisp.p));
						}
					}
					else
					{
						pDisp = i->second;
					}
					//replace the colon.
					*wColon = L':';
					wColon++;
					//wColon now starts immediately after the colon.
					if(pDisp) {
						// Check that the function exists in the liberary
						DISPID dispid;
						hr = pDisp->GetIDsOfNames(IID_NULL,&wColon,1,LOCALE_SYSTEM_DEFAULT,&dispid);
						if(DISP_E_UNKNOWNNAME == hr) 
						{
							CString sReason;
							sReason.Format("Function \"%S\" not found in library \"%S\"", wColon, bsURI);
							onComputeException(sReason);
						}
					} 
					else
					{
						hr = DISP_E_UNKNOWNNAME;
						CString sReason;
						sReason.Format("Could not load library \"%S\"", bsURI);
						onComputeException(sReason);
					}
				}
				else
				{
					//otherwise, look up the default namespace.
					//This cannot be done using the NSResolver in DOM3XPath
					//	as you are not allowed default namespaces in XPath.
					//	I am therefore leaving this branch empty.

					CString sReason;
					sReason.Format("Function \"%S\" not found (no library)",wFunction);
					onComputeException(sReason);
				}
				wFunction = wcstok(NULL, L" ");
			}
		}
	}
	return hr;
}

CImplInstance * CImplModel::getInstance(BSTR sInstanceId)
{
	//Wait until the instances are loaded.
	//	Note that this is (and belongs) outside the if!
	//	It cannot be assumed that the first instance to be loaded
	//	is the default instance, i.e. the first in doc order.
	//If the default instance is huge, and subsequent instances
	//	are small, as might happen if a form author has a control
	//	instance for things like model-based switching.
	
/*	DWORD dwWaitResult = WaitForMultipleObjects( 
		m_lInstances,   // number of handles in array
		m_harrInstanceLoadEvents,  // array of read-event handles
		TRUE,         // wait until all are signaled
		INFINITE);    // indefinite wait
*/
	CImplInstance * pInstance=0;
	if(sInstanceId && sInstanceId[0])
	{
		CMapBSTRToInstancePtr::iterator i =  m_Instances.find(sInstanceId);
		if(i != m_Instances.end())
			pInstance = i->second;
	}
	else
	{
		//return the first instance.
		pInstance =  m_pInstance;
	}
	return pInstance;
}

HRESULT CImplModel::addInstance(BSTR id, CImplInstance *pInstance)
{
	HRESULT hr;
	if(pInstance)
	{
		if(!m_pInstance)
		{
			//Save the default instance if it has not already been saved.
			m_pInstance = pInstance;
		}

		//add to the list of instances.

		//make sure the name is not already taken.
		hr = m_pXForms->AddInstance(pInstance);
		if(SUCCEEDED(hr))
		{
			CComBSTR sId;
			//If an anonymous instance has been requested to be added, 
			//	Give it a fake
			if(!id || !id[0])
			{
				CStringW sGeneratedId;
				sGeneratedId.Format(L"id_ptr[%i]",pInstance);
				sId.Attach(sGeneratedId.AllocSysString());
			}
			else
				sId = id;
			//add to the list in this model.
			m_Instances.insert(CPairBSTRInstancePtr(sId,pInstance));
			hr = S_OK;
		}
	}
	else
	{
		hr = E_INVALIDARG;
	}
	return hr;

}

HRESULT CImplModel::findInstance(IXMLDOMNode *pNode, CImplInstance **ppInstance)
{
	//Get the unknown pointer of the ownerdocument to lookup in the map
	CComPtr<IXMLDOMDocument> pDoc;
	HRESULT hr = pNode->get_ownerDocument(&pDoc);
	if(SUCCEEDED(hr) && pDoc)
	{
		
		IUnknown * pUnk = 0;
		hr = pDoc->QueryInterface(IID_IUnknown,(void**)&pUnk);
		if(SUCCEEDED(hr) && pUnk)
		{
			CImplInstance * pInst = 0;
			//look up the unknown pointer in the map
			if(m_UnkInstances.Lookup(pUnk,(void*&)pInst))
			{
				//if it was found, return the found pointer
				*ppInstance  = pInst;
				hr = S_OK;
			}
			else
			{
				//otherwise, it is null
				*ppInstance = 0;
				hr = S_FALSE;
			}
		}
		::safeRelease(pUnk);
	}
	return hr;
}

void CImplModel::onInstanceReset(CImplInstance *pInst, CDOM3Document *pDoc)
{
	AddDocumentToLookupTable(pInst, pDoc);
}

void CImplModel::RemoveDocumentFromLookupTable(CDOM3Document *pDoc) {
    if(pDoc && pDoc->m_pXMLParser) {
		// Get the IUnknown address for the XML Document
		IUnknown * pUnk = 0;
		pDoc->m_pXMLParser->QueryInterface(IID_IUnknown,(void**)&pUnk);
		// Remove the document from the instance map.
		m_UnkInstances.RemoveKey((void *) pUnk);
		// Also we need to remove it from the model map
		if(m_pXForms)
			m_pXForms->RemoveModelDocument(pUnk);
		// Cleanup
		::safeRelease(pUnk);
	}
}

void CImplModel::AddDocumentToLookupTable(CImplInstance *pInst, CDOM3Document *pDoc)
{
	//The implementation of this function means that the map will increase 
	//	in size each time reset is called on the instance.
	//	This is because we have lost the pointer to the old document, and can't 
	//	so easily remove it.
	//The consequences of this are not so dire, pUnk is just a number, it does not
	//	hold a refcount to the document, so its deletion is independent of this
	//	member

	//TODO: fix the problem in the above comment.  Possibly use two maps, 
	//		or find a sensible way of keeping hold of IUnknown for the old 
	//		document to do removeKey.
	//		Probably use two functions, eg this and onBeforeInstanceChangeDocument.

	if(pDoc && pInst && pDoc->m_pXMLParser)
	{
		//Get IUnknown of the document to use as a key.
		IUnknown * pUnk = 0;
		pDoc->m_pXMLParser->QueryInterface(IID_IUnknown,(void**)&pUnk);
		//insert new key/value pari into the map into the map,
		m_UnkInstances.SetAt(pUnk,pInst);
		if(m_pXForms)
			m_pXForms->SetModelDocument(this,pUnk);
		//cleanup, we only need pUnk as a number for comparison,
		//	not as a refcount-holding pointer to the object.
		::safeRelease(pUnk);
	}
}

void CImplModel::notifyDelete()
{

}

void CImplModel::deleteControl(CFormControl *pControl)
{
	//Places control into the toDelete list.
	//	Cares not for whether it is in the
	//	live list or not.
	if(!m_DeadFormControls.Find(pControl))
		m_DeadFormControls.AddHead(pControl);
}

HRESULT CImplModel::burnDeadControls()
{
	HRESULT hr = S_OK;
	while(!m_DeadFormControls.IsEmpty())
	{
		CFormControl * pFc = m_DeadFormControls.RemoveHead();
		POSITION pos = m_FormControls.Find(pFc);
		
		//We must be able to find the control.
		ASSERT(pos!=0);
		
		if(pos)
		{
			m_FormControls.RemoveAt(pos);
			pos = m_pendingControls.Find(pFc);
			if(pos)
			{
				m_pendingControls.RemoveAt(pos);
			}

			pFc->RequestDeleteAtEarliestConvenience();
		}
	}

	return hr;
}

HRESULT CImplModel::GetDefaultEvaluationContext(IXMLDOMNode **ppNode)
{
	HRESULT hr = E_FAIL;
	if(m_pInstance)
	{
		CDOM3Document * pDomLive = m_pInstance->GetLiveDOM();
		if(pDomLive)
		{
			CComPtr<IXMLDOMElement> pEl;
			HRESULT _hr = pDomLive->m_pXMLParser->get_documentElement(&pEl);
			if(SUCCEEDED(_hr) && pEl)
			{
				_hr = pEl->QueryInterface(IID_IXMLDOMNode,(void**)ppNode);
				hr = _hr;
			}
		}
	}
	return hr;

}

HRESULT CImplModel::initializeNewControls(void)
{
	//	returns:
	//	S_OK = model has been completely initialized on a previous run.
	//	S_FALSE = some controls were initialized on this run, more controls
	//			may need initializing.
	HRESULT hr = S_OK;
	POSITION pos = m_FormControls.GetHeadPosition();

	while (pos != NULL)
	{
		CImplNodeBinding * pControl = (CImplNodeBinding*)m_FormControls.GetNext(pos);
		//After submit where replace= instance, everything is reinitialized.
		//	So whenever model::uiinitialize occurs, Force the initialisation
		//	of FormControls.
		HRESULT _hr = S_FALSE;
		if(pControl && !pControl->m_bInParentContext)
			_hr = pControl->initialize(false);
		if(_hr == S_OK)
		{
			//A return value of S_OK from CFormControl::initialize
			//	means that an initialization has occured.
			hr = S_FALSE;
		}
	}

	return hr;
}

// Called to add a vertex from the masterDependecy tree to the pertinent subgraph
HRESULT CImplModel::makePertinent(CVertex * pVertex)
{
	HRESULT hr = E_FAIL;
	if(m_pMasterDependencyList)
	{	
		if(pVertex)
		{
			//Temp, until proper subgraph and dependencies are implemented.
			hr = S_OK;
			m_pPertinentSubGraph = m_pMasterDependencyList;
		}
		else
		{
			//asking to make NULL pertinent 
			//	is a request to flag the whole graph for recalculation.
			hr = S_OK;
			m_pPertinentSubGraph = m_pMasterDependencyList;
		}
	}
	
	return hr;
}

// Initialises any controls bound to this model that have not yet been initialised.
//	Returns true if any initialisations occured during this call.
bool CImplModel::initializePendingControls(void)
{
	bool bReturn = !m_pendingControls.IsEmpty();
	
	while(!(m_pendingControls.IsEmpty()))
	{
		CFormControl * pControl = m_pendingControls.RemoveHead();
		if(pControl)
			pControl->initialize(false);
	}

	return bReturn;
}

// principally called by CImplActionSetValue
HRESULT CImplModel::setValue(BSTR const sBind , BSTR const sRef , BSTR const sValueRef , BSTR const sLiteral , IXMLDOMNode * pEvaluationContext)
{
		//currently, sValue is the XPath expression pointing to the value
	// sValue should be the ACTUAL VALUE.  This will give better control
	//	and separation
	HRESULT hr = S_OK;
	CComBSTR bsRef;
	CComPtr<IXMLDOMNode> pContext;
	if(!::isEmpty(sBind))
	{
		CImplBind * pBind = 0;
		m_pXForms->getBind(sBind,pBind);
		if(pBind)
		{
			if(pBind->m_instVertices.GetUpperBound() > -1)
			{
				CVertex * pVx = pBind->m_instVertices.GetAt(0);
				if(pVx)
				{
					CComBSTR bsData;
					//HACK should go through the bind.	
					bsRef = L".";
					pVx->m_pInstanceNode->getNode(&pContext);
				}
			}
			else
			{
				hr = S_FALSE;
			}
		}
	}
	else
	{
		bsRef = sRef;
		pContext = pEvaluationContext;
	}

	if(!::isEmpty(bsRef))
	//lack of sRef property should be a parse error,
	//	and should never get this far without it.
	{
		CComPtr<IXMLDOMNode> pNodeTarget;
		getInstanceNode(bsRef,pContext,false,&pNodeTarget);
		if(pNodeTarget)
		{


			if(!::isEmpty(sValueRef))
			{
				CComBSTR sData;
				CComPtr<IXPathResult> pData;
				
				//get the source data
				hr = getInstanceData(sValueRef,pNodeTarget,&pData);

				if(SUCCEEDED(hr))
				{
					//put the source data into the target node
					hr = putInstanceData(pNodeTarget, pData);
				}
				// release the Result object.
			}
			else
			{
				//TODO: this bypasses the BoundNode mechanism.
				//		rebuild is probably required here too.
				//		alternatively bind setvalue elements 
				//		into the dependency tree like controls.
				hr = putInstanceString(bsRef,pContext,sLiteral);
			}
			//allow the model to refresh its UI.
			//rebuild(m_pXForms->IsInDeferredUpdate()); //This is erroneous, why is it here?
			recalculate(m_pXForms->IsInDeferredUpdate());
			revalidate(m_pXForms->IsInDeferredUpdate());
			refresh(m_pXForms->IsInDeferredUpdate());
		}
	}

	return hr;
}

void CImplModel::onComputeException(const char * const sReason)
{
	//notify the host and run custom event handlers.
	FireEvent(EVENT_COMPUTEEXCEPT,true,false);

	m_pXForms->reportException(sReason);
}

// <summary>Fires the ready event using both the event notification systems</summary>
HRESULT CImplModel::ready(void)
{
	return FireEvent(EVENT_READY);
}

HRESULT CImplModel::GetEvaluator(IXPathEvaluator ** ppEvaluator)
{	
	HRESULT hr = E_FAIL;
	if(m_pInstance)
		hr = m_pInstance->GetEvaluator(ppEvaluator);
	return hr;
}
// <summary>fetches a numeric value for at in insert and delete</summary>
HRESULT CImplModel::getNodesetActionLocation(IXMLDOMNode * pContext,BSTR const bsAt,const unsigned long max, unsigned long * pulRetval)
{
	double dAt = 0;
	CComBSTR sAt(L"round(");
	sAt += bsAt;
	sAt += L")";
	//TODO: add context to getInstanceNumber,
	//	requires context to be added to CImplRepeat::
	//	setCursor(BSTR)
	HRESULT hr = getInstanceNumber(sAt, &dAt,pContext);
	if (S_OK == hr)
	{
		// NB:	originally correct, but as of W3C Working Draft 21 August 2002, 
		//		The behaviour regarding out-of-range and NaN indices
		//		is different for delete and insert.  Be Careful.
		unsigned long ulAt;
		//if NaN, Append
		if(_isnan(dAt))
			ulAt = max;
		else
		{
			//convert dAt from 1-start to 0-start
			dAt-= 1;

			//if out of range below, prepend,
			if (dAt <= 0)
				ulAt = 0;
			//if out of range above, append
			else if (dAt > max)
				ulAt = max;
			//otherwise use given position.
			else
				ulAt = static_cast<unsigned long>(dAt);
		}
		*pulRetval = ulAt;
	}
	return hr;
}

HRESULT CImplModel::ConfigureForm(void) const
{
	HRESULT hr = S_OK;

	return hr;
}

HRESULT SetSelectionNamespacesOfOwnerDocument(IXMLDOMNode * pNode,VARIANT vtNewNamespaces,VARIANT * pvtOldNamespaces)
{
	HRESULT hr;
	if(pNode)
	{
		CComQIPtr<IXMLDOMDocument> pDoc;
		hr = pNode->get_ownerDocument(&pDoc);
		
		//node may be a document itself, if ownerdocument returned NULL.
		CComQIPtr<IXMLDOMDocument2> pDoc2(!pDoc?pNode:pDoc);
		
		if(pDoc2)
		{
			CComBSTR sProperty(L"SelectionNamespaces");
			if(pvtOldNamespaces)
				pDoc2->getProperty(sProperty,pvtOldNamespaces);
			hr = pDoc2->setProperty(sProperty,vtNewNamespaces);
		}
		else
		{
			hr = E_NOINTERFACE;
		}
	}
	else
	{
		hr = E_INVALIDARG;
	}
	return hr;
}

// <summary>removes xsi:type attributes from anywhere in pNode</summary><remarks>ALWAYS use paired with replaceXSITypeAttributes, as it leaves placeholders</remarks>
HRESULT CImplModel::removeXSITypeAttributes(IXMLDOMNode * pNode) const
{
	CComVariant sOldNS, sNewNs(L"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
	SetSelectionNamespacesOfOwnerDocument(pNode,sNewNs,&sOldNS);
	CComBSTR sQuery(L"descendant-or-self::*[@xsi:type]");
	CComPtr<IXMLDOMNodeList> pNodeset;
	HRESULT hr = pNode->selectNodes(sQuery,&pNodeset);
	
	if(pNodeset)
	{
		CComPtr<IXMLDOMNode> pXSITypeNode;
		CComPtr<IXMLDOMNode> pNodeWithXSIType;
		pNodeset->nextNode(&pNodeWithXSIType);
		CComBSTR sNewNameForTypeAttribute(L"TypeAttributePlaceholderFudge");
		while(pNodeWithXSIType)
		{
			CComQIPtr<IXMLDOMElement> pEl = pNodeWithXSIType;
			if(pEl)
			{
				//removeAttribute is not namespace aware.
				pNodeWithXSIType->selectSingleNode(L"@xsi:type",&pXSITypeNode);
				if(pXSITypeNode) {
					CComBSTR bsText;
					pXSITypeNode->get_text(&bsText);
					CComQIPtr<IXMLDOMAttribute> pAttribute = pXSITypeNode;
					pEl->removeAttributeNode(pAttribute,0);
					pEl->setAttribute(sNewNameForTypeAttribute,CComVariant(bsText));
					// Bug 815 - Need to Release otherwise we could end up leaking.
					pXSITypeNode.Release();
				}
			}
			pNodeWithXSIType.Release();
			pNodeset->nextNode(&pNodeWithXSIType);
		}
	}
	SetSelectionNamespacesOfOwnerDocument(pNode,sOldNS,0);
	return hr;
}

// <summary>replaces xsi:type attributes removed by removeXSITypeAttributes</summary>
HRESULT CImplModel::replaceXSITypeAttributes(IXMLDOMNode * pNode) const
{
	CComBSTR sQuery(L"descendant-or-self::*[@TypeAttributePlaceholderFudge]");
	CComPtr<IXMLDOMNodeList> pNodeset;
	HRESULT hr = pNode->selectNodes(sQuery,&pNodeset);
	if(pNodeset)
	{
		CComPtr<IXMLDOMDocument> pDoc;
		pNode->get_ownerDocument(&pDoc);
		if(pDoc)
		{
			CComPtr<IXMLDOMNode> pNodeWithXSIType;
			CComPtr<IXMLDOMNode> pXSITypeNode;
			pNodeset->nextNode(&pNodeWithXSIType);
			CComBSTR sNewNameForTypeAttribute(L"TypeAttributePlaceholderFudge");
			while(pNodeWithXSIType)
			{
				CComQIPtr<IXMLDOMElement> pEl = pNodeWithXSIType;
				if(pEl)
				{
					// Create a new xsi:type attribute.
					pDoc->createNode(CComVariant(NODE_ATTRIBUTE),L"type",L"http://www.w3.org/2001/XMLSchema-instance",&pXSITypeNode);
					if(pXSITypeNode) {
						CComVariant vtValue;
						CComBSTR bsText;
						// Bug 814 - Get the value of the temporary attribute
						pEl->getAttribute(sNewNameForTypeAttribute, &vtValue);
						if(vtValue.vt == VT_BSTR)
							vtValue.CopyTo(&bsText);
						// Bug 814 - Set the value of the new xsi:type attribute
						pXSITypeNode->put_text(bsText);
						// Remove the temporary attribute
						pEl->removeAttribute(sNewNameForTypeAttribute);
						// Add the xsi:type back to the tree.
						CComQIPtr<IXMLDOMAttribute> pAttribute = pXSITypeNode;
						pEl->setAttributeNode(pAttribute,0);
						// Release so that we don't leak when calling createNode().
						pXSITypeNode.Release();
					}
				}
				pNodeWithXSIType.Release();
				pNodeset->nextNode(&pNodeWithXSIType);
			}
		}
	}
	return hr;
}



HRESULT CImplModel::getInstanceString(BSTR sXPath, CNode* pContextNode, BSTR* psData)
{
	HRESULT hr = S_FALSE;

	if (m_pInstance)
		hr = m_pInstance->getInstanceString(sXPath, pContextNode, psData);

	return hr;
}